Transfer Ücreti Uzantısı
Özet
- Token Uzantı Programı'nın
transfer ücreti
uzantısı, her transferde ücretlerin tutulmasına olanak tanır. Bu ücretler alıcının hesabında saklanır ve yalnızcawithdrawWithheldAuthority
yetkisi ile tahsil edilebilir. - Tutulan token'lar doğrudan alıcının hesaplarından çekilebilir veya mint'e toplanıp daha sonra çekilebilir.
transfer ücreti
uzantısını kullanan mint'lerle yapılan işlemlerdetransferCheckedWithFee
talimatı kullanılması gerekir.
Genel Bakış
Diyelim ki bir Solana oyun geliştiricisiniz ve büyük bir açık dünya çok oyunculu rol yapma oyunu yapıyorsunuz. Bu oyunda, tüm oyuncuların kazanıp ticaret yapacağı bir para birimi olacaktır. Oyun ekonomisini sirküler hale getirmek için, bu para birimi el değiştirdiğinde her seferinde küçük bir transfer ücreti talep etmek isteyebilirsiniz, buna geliştirici vergisi diyorsunuz. :::info İlginç olanı, bu her transferde, oyunun içinde ve dışında çalışacaktır!
Token Uzantı Programı'nın transfer ücreti
uzantısı, bir mint üzerinde
transfer ücreti yapılandırmanıza olanak tanır; böylece ücretler protokol düzeyinde
değerlendirilir. Her transferde, o mint'in belirli bir miktarı alıcının
hesabında tutulur ve alıcı tarafından kullanılamaz. Transferden hemen sonra,
withdraw
yetkisi bu tutulan token'ları talep edebilir.
transfer ücreti
uzantısı özelleştirilebilir ve güncellenebilir. Gireceğimizparametreler şunlardır:
- Ücret temel puanları: Her transferde uygulanan ücrettir. Örneğin, 1000 token 50 temel puan ile transfer edilirse, 5 token elde edilir.
- Maksimum ücret: Transfer ücretleri için üst sınırdır. 5000 token maksimum ücreti ile, 10.000.000.000.000 token'lık bir transfer yalnızca 5000 token getirecektir.
- Transfer ücreti yetkisi: Ücretleri değiştirebilen varlık.
- Tutulan yetkiyi çekme: Mint veya token hesaplarında tutulan token'ları hareket ettirebilen varlık.
Ücret temel puanlarını hesaplama
Uzantıya girmeden önce, "ücret temel puanları" hakkında hızlı bir tanıtım.
Bir temel puan, finansal bir aracın değerindeki veya oranındaki yüzdelik değişimi tanımlamak için finansal alanda kullanılan bir ölçüm birimidir. Bir temel puan, 0,01% veya ondalık biçimde 0,0001'e eşittir.
Ücreti hesaplamak için şu şekilde bir hesaplama yapmalıyız:
Ücret = (token_miktarı * ücret_taban_puanları) / 10000
Sabit olan 10.000, ücret temel puan yüzdesini eşdeğer tutara dönüştürmek için kullanılır.
Transfer ücreti ile mint yapılandırma
transfer ücreti
uzantısıyla bir mint başlatmak, üç talimatı içerir:
SystemProgram.createAccount
createInitializeTransferFeeConfigInstruction
createInitializeMintInstruction
Birinci talimat SystemProgram.createAccount
, blockchain üzerinde mint hesabı
için alan ayırır. Bu talimat üç şeyi gerçekleştirir:
alan
ayırır- Kiralama için
lamports
transfer eder - Kendisine ait programa atama yapar
Token Uzantı Programı'nın tüm mint'lerinde olduğu gibi, mint için gereken alan ve
lamports miktarını hesaplamamız gerekiyor. Bunları getMintLen
ve
getMinimumBalanceForRentExemption
kullanarak alabiliriz.
const extensions = [ExtensionType.TransferFeeConfig];
const mintLength = getMintLen(extensions);
const mintLamports =
await connection.getMinimumBalanceForRentExemption(mintLength);
const createAccountInstruction = SystemProgram.createAccount({
fromPubkey: payer.publicKey,
newAccountPubkey: mintKeypair.publicKey,
space: mintLength,
lamports: mintLamports,
programId: TOKEN_2022_PROGRAM_ID,
});
İkinci talimat createInitializeTransferFeeConfigInstruction
, transfer ücreti
uzantısını başlatır.
Şunları gerektiren parametreleri alır:
mint
: Token mint hesabıtransferFeeConfigAuthority
: Ücretleri güncelleyebilen isteğe bağlı yetkiwithdrawWithheldAuthority
: Ücretleri çekebilen isteğe bağlı yetkitransferFeeBasisPoints
: Transfer miktarının temel puanları cinsinden toplanan transfer ücreti miktarımaximumFee
: Transferler için uygulanan maksimum ücretprogramId
: SPL Token program hesabı
const initializeTransferFeeConfigInstruction =
createInitializeTransferFeeConfigInstruction(
mintKeypair.publicKey,
payer.publicKey,
payer.publicKey,
feeBasisPoints,
maxFee,
TOKEN_2022_PROGRAM_ID,
);
Üçüncü talimat createInitializeMintInstruction
, mint'i başlatır.
const initializeMintInstruction = createInitializeMintInstruction(
mintKeypair.publicKey,
decimals,
payer.publicKey,
null,
TOKEN_2022_PROGRAM_ID,
);
Son olarak, tüm bu talimatları bir işlemle eklemeli ve blockchain'e göndermelisiniz.
const mintTransaction = new Transaction().add(
createAccountInstruction,
initializeTransferFeeConfigInstruction,
initializeMintInstruction,
);
const signature = await sendAndConfirmTransaction(
connection,
mintTransaction,
[payer, mintKeypair],
{ commitment: "finalized" },
);
Transfer Ücretleri ile Mint Aktarma
transfer ücreti
uzantısını kullanarak token'ları aktarırken dikkate almanız
gereken birkaç nokta vardır.
İlk olarak, alıcı "ücretin" ödenmesinden sorumludur. Eğer 100 token'ı 500 temel puan (5%) ile gönderirsem, alıcı 95 token alır (beş tutulan).
İkinci olarak, ücret, gönderilen token miktarına göre değil, ilgili token'ın en
küçük birimi üzerinden hesaplanır. Solana programlamasında, her zaman en küçük
birimde transfer, mint veya yanma işlemleri için miktarları belirtiriz. Birine bir
SOL göndermek istiyorsak, aslında 1 * 10 ^ 9
lamports gönderiyoruz. Diğer bir
deyişle, bir ABD doları göndermek isterseniz, aslında 100 kuruş gönderiyorsunuz.
Bu doları 500 temel puan (5%) transfer ücreti ile bir token haline getirelim. Bir
dolar göndermek, beş centlik bir ücrete neden olur. Şimdi maksimum ücretin 10
cent olduğunu varsayalım, bu her zaman en yüksek ücret olacaktır, her ne kadar
$10.000 gönderdiğimizde bile.
Hesaplama: Bu, transfer ücretlerinin nasıl hesaplandığını anlamanıza güç sağlayacaktır.
const transferAmount = BigInt(tokensToSend * 10 ** decimals);
const basisPointFee =
(transferAmount * BigInt(feeBasisPoints)) / BigInt(10_000);
const fee = basisPointFee > maxFee ? maxFee : basisPointFee;
Üçüncü ve son olarak, transfer ücreti
uzantısıyla token'ları aktarmanın iki yolu
vardır: transfer_checked
veya transfer_checked_with_fee
. Normal
transfer
fonksiyonu, ücretleri yönetmek için gerekli mantığı içermez.
Transfer yapmak için kullanabileceğiniz fonksiyonlardan biri:
transfer_checked_with_fee
: Ücretleri hesaplamalı ve doğru şekilde sağlamalısınız.transfer_checked
: Bu size ücretleri hesaplayacaktır.
/**
* Bir hesaptan diğer bir hesaba token transferi yapın, token mint ve ondalık sayılarını doğrulayarak
*
* @param connection Kullanılacak bağlantı
* @param payer İşlem ücretlerini ödeyen
* @param source Kaynak hesap
* @param mint Hesap için mint
* @param destination Hedef hesap
* @param owner Kaynak hesabın sahibi
* @param amount Aktarılacak token sayısı
* @param decimals Aktarım miktarındaki ondalık sayısı
* @param multiSigners Eğer `owner` çoklu imza ise imza atan hesaplar
* @param confirmOptions İşlemi onaylama seçenekleri
* @param programId SPL Token program hesabı
*
* @return Onaylanmış işlemin imzası
*/
const secondTransferAmount = BigInt(1 * 10 ** decimals);
const secondTransferSignature = await transferChecked(
connection,
payer,
sourceAccount,
mint,
destinationAccount,
sourceKeypair,
secondTransferAmount,
decimals, // Ayrıca mint hesap detaylarını `getMint(...)` ile alabilirsiniz
[],
undefined,
TOKEN_2022_PROGRAM_ID,
);
Ücretleri Toplama
Tutulan token hesaplarından "ücretleri toplamanın" iki yolu vardır.
withdrawWithheldAuthority
, bir kullanıcının token hesabının tutulan kısmından doğrudan çekiş yapabilir.- Tutulan token'ları toplayabilir ve bunları mint hesabında saklayabiliriz; bu
sayede istediği zaman
withdrawWithheldAuthority
ile çekebilir.
Ama önce, neden bu iki seçeneğe sahip olunuyor?
Basitçe ifade etmek gerekirse, doğrudan çekiş yapmak, yetkilendirilmiş bir
işlevdir, yani yalnızca withdrawWithheldAuthority
bunu çağırabilir. Oysa
toplama, izinsizdir; bu, toplama fonksiyonunu çağırarak tüm ücretlerin mint'e
girilmesine izin verir.
- Mint oluşturucusunun ücretin nereye gideceğini bilmek isteyebilir.
- Bu bir darboğaz yaratabilir.
Diyelim ki transfer ücreti
etkin bir token'ınız var ve ücret kasanız bu
ücretleri alıyorsa. Eğer binlerce insan aynı anda token ile işlem yapmaya çalışıyorsa,
hepsi de sizin ücret kasanızın bakiyesini güncellemek zorunda kalacak. Ücret
kasası "yazılabilir" olmalıdır. İlginçtir ki, Solana paralel işleme yapabilir,
ancak aynı hesaplara aynı anda yazma işlemi gerçekleştirilemez. Bu nedenle, bu
binlerce kişi sırada beklemek zorunda kalacak ve transferi büyük ölçüde
yavaşlatacaktır. Bu, tutulan
transfer ücretlerinin alıcının hesabında
tutulmasıyla çözülmektedir; böylece yalnızca gönderici ve alıcının hesapları
yazılabilir. Sonrasında withdrawWithheldAuthority
, istediği zaman ücret
kasasına çekim yapabilir.
Doğrudan Ücret Çekme
İlk durumda, tüm tutulan transfer ücretlerini doğrudan tüm token hesaplarından çekmek istiyorsak, aşağıdakileri yapabiliriz:
- Mint ile ilişkili tüm token hesaplarını
getProgramAccounts
ile alın. - Tutulan token'lara sahip tüm token hesaplarını bir listeye ekleyin.
withdrawWithheldTokensFromAccounts
fonksiyonunu çağırın (yetki bir imza sahibidir).
// verilen mint için tüm token hesaplarını alır
const accounts = await connection.getProgramAccounts(TOKEN_2022_PROGRAM_ID, {
commitment: "finalized",
filters: [
{
memcmp: {
offset: 0,
bytes: mint.toString(),
},
},
],
});
const accountsToWithdrawFrom = [];
for (const accountInfo of accounts) {
const unpackedAccount = unpackAccount(
accountInfo.pubkey,
accountInfo.account,
TOKEN_2022_PROGRAM_ID,
);
// Eğer tutulan token varsa, onu listeye ekleyin
const transferFeeAmount = getTransferFeeAmount(unpackedAccount);
if (
transferFeeAmount != null &&
transferFeeAmount.withheldAmount > BigInt(0)
) {
accountsToWithdrawFrom.push(accountInfo.pubkey);
}
}
/**
* Hesaplardan tutulan token'ları çekin
*
* @param connection Kullanılacak bağlantı
* @param payer İşlem ücretlerini ödeyen
* @param mint Token mint'i
* @param destination Hedef hesap
* @param authority Mint'in tutulan token'ları çekme yetkisi
* @param multiSigners Eğer `owner` çoklu imza ise imza atan hesaplar
* @param sources Tutulan ücretleri çekmek için kaynak hesaplar
* @param confirmOptions İşlemi onaylama seçenekleri
* @param programId SPL Token program hesabı
*
* @return Onaylanmış işlemin imzası
*/
await withdrawWithheldTokensFromAccounts(
connection,
payer,
mint,
feeVaultAccount,
authority,
[],
accountsToWithdrawFrom,
{ commitment: "finalized" },
TOKEN_2022_PROGRAM_ID,
);
Ücretleri Toplama
İkinci yaklaşımımız "toplama" - bu, izinsiz bir işlevdir; yani herkes bunu
çağırabilir. Bu yaklaşım, clockwork gibi araçlarla
toplama talimatının çalıştırılması için idealdir. Fark, toplama yaptığımızda,
tutulan token'ların mint içinde saklandığıdır. Sonrasında withdrawWithheldAuthority
,
bu token'ları mint üzerinden istediği zaman çekebilir.
Toplama işlemi yapmak için:
- Tıpkı yukarıdaki gibi toplamak istediğiniz tüm hesapları toplayın.
harvestWithheldTokensToMint
fonksiyonunu çağırın.- Mint'ten çekmek için
withdrawWithheldTokensFromMint
fonksiyonunu çağırın.
/**
* Hesaplardan mint'e tutulan token'ları toplayın
*
* @param connection Kullanılacak bağlantı
* @param payer İşlem ücretlerini ödeyen
* @param mint Token mint'i
* @param sources Tutulan ücretleri çekmek için kaynak hesaplar
* @param confirmOptions İşlemi onaylama seçenekleri
* @param programId SPL Token program hesabı
*
* @return Onaylanmış işlemin imzası
*/
await harvestWithheldTokensToMint(
connection,
payer,
mint,
accountsToHarvestFrom,
{ commitment: "finalized" },
TOKEN_2022_PROGRAM_ID,
);
/**
* Mint'ten tutulan token'ları çekin
*
* @param connection Kullanılacak bağlantı
* @param payer İşlem ücretlerini ödeyen
* @param mint Token mint'i
* @param destination Hedef hesap
* @param authority Mint'in tutulan token'ları çekme yetkisi
* @param multiSigners Eğer `owner` çoklu imza ise imza atan hesaplar
* @param confirmOptions İşlemi onaylama seçenekleri
* @param programId SPL Token program hesabı
*
* @return Onaylanmış işlemin imzası
*/
await withdrawWithheldTokensFromMint(
connection,
payer,
mint,
feeVaultAccount,
authority,
[],
{ commitment: "finalized" },
TOKEN_2022_PROGRAM_ID,
);
Ücret Güncelleme
Şu anda, JS kütüphanesi ile oluşturulduktan sonra transfer ücretini
değiştirmenin
bir yolu yoktur. Ancak, CLI ile bunu yapabilirsiniz; bu durumda solana config
cüzdanınızın sonucu transferFeeConfigAuthority
olmalıdır:
solana address
## Yukarıda belirtilen ^ sonucu `transferFeeConfigAuthority` olmalıdır
spl-token set-transfer-fee <MINT_ID> <FEE_IN_BASIS_POINTS> <MAX_FEE>
Yetkileri Güncelleme
transferFeeConfigAuthority
veya withdrawWithheldAuthority
'yi
değiştirmek istiyorsanız, setAuthority
fonksiyonuyla bunu yapabilirsiniz.
Doğru hesapları ve authorityType
'ı geçmelisiniz; bu durumlarda ise
TransferFeeConfig
ve WithheldWithdraw
'dır.
/**
* Hesaba yeni bir yetki atayın
*
* @param connection Kullanılacak bağlantı
* @param payer İşlem ücretlerini ödeyen
* @param account Hesabın adresi
* @param currentAuthority Mevcut yetki türü
* @param authorityType Ayarlanacak yetki türü
* @param newAuthority Hesabın yeni yetkisi
* @param multiSigners Eğer `currentAuthority` çoklu imza ise imza atan hesaplar
* @param confirmOptions İşlemi onaylama seçenekleri
* @param programId SPL Token program hesabı
*
* @return Onaylanmış işlemin imzası
*/
await setAuthority(
connection,
payer,
mint,
currentAuthority,
AuthorityType.TransferFeeConfig, // veya AuthorityType.WithheldWithdraw
newAuthority,
[],
undefined,
TOKEN_2022_PROGRAM_ID,
);
Laboratuvar
Bu laboratuvar çalışmasında, transfer ücreti yapılandırılmış bir mint oluşturacağız. Ücretleri tutmak için bir ücret kasası kullanacağız ve hem doğrudan yöntemle hem de toplama yöntemleriyle ücret toplayacağız.
1. Başlarken
Başlamak için transfer-fee
isimli boş bir dizin oluşturun ve o dizine gidin.
Yeni bir proje başlatacağız. npm init
komutunu çalıştırın ve yönergeleri izleyin.
Sonra, bağımlılıklarımızı eklememiz gerekecek. Gerekli paketleri yüklemek için aşağıdakileri çalıştırın:
npm i @solana-developers/helpers@2 @solana/spl-token @solana/web3.js@1 esrun dotenv typescript
src
isminde bir dizin oluşturun. Bu dizinde index.ts
isminde bir dosya
oluşturun. Bu, bu uzantının kurallarını kontrol edeceğimiz yer olacak. index.ts
dosyasına aşağıdaki kodu yapıştırın:
import { Connection, Keypair } from "@solana/web3.js";
import { initializeKeypair } from "@solana-developers/helpers";
import { transferCheckedWithFee } from "@solana/spl-token";
/**
* Bir bağlantı oluşturun ve bir keypair yoksa başlatın.
* Eğer bir keypair varsa, gerekirse bir SOL token'ı airdrop'layın.
*/
const connection = new Connection("http://127.0.0.1:8899");
const payer = await initializeKeypair(connection);
console.log(`public key: ${payer.publicKey.toBase58()}`);
const mintKeypair = Keypair.generate();
const mint = mintKeypair.publicKey;
console.log("\nmint public key: " + mintKeypair.publicKey.toBase58() + "\n\n");
// TRANSFER ÜCRETİ İLE MINT OLUŞTURUN
// ÜCRET KASASI HESABI OLUŞTURUN
// BİR KAYNAK HESABI OLUŞTURUN VE TOKEN MINT EDİN
// HEDEF HESABI OLUŞTURUN
// TOKEN'LARI AKTARIN
// TUTULAN TOKEN'LARLA HESAPLARI GETİRİN
// TUTULAN TOKEN'LARI ÇEKİN
// GÜNCELLENEN ÜCRET KASASI BAKİYESİNİ DOĞRULAYIN
// TUTULAN TOKEN'LARI MINT'E TOPLAYIN
// TOPLANAN TOKEN'LARI ÇEKİN
// GÜNCELLENEN ÜCRET KASASI BAKİYESİNİ DOĞRULAYIN
index.ts
, belirtilen doğrulayıcı düğüme bir bağlantı oluşturan ve initializeKeypair
işlevini çağıran bir ana işlevi içerir. Bu main
işlevi, script'imizi yazacağımız
yer olacaktır.
Gidin ve script'i çalıştırın. Terminalinizde mint
public anahtarının
görüntülendiğini görmelisiniz.
esrun src/index.ts
Eğer initializeKeypair
ile airdrop sırasında bir hata alırsanız, bir sonraki
aşamada ilerleyin.
2. Doğrulayıcı düğüm çalıştırma
Bu kılavuz için kendi doğrulayıcı düğümümüzü çalıştıracağız.
Ayrı bir terminalde, aşağıdaki komutu çalıştırın: solana-test-validator
. Bu,
düğümü çalıştıracak ve bazı anahtarları ve değerleri de kaydedecektir. Bağlantımızda
kullanmak üzere almamız gereken değer, JSON RPC URL'sidir; bu durumda bu http://127.0.0.1:8899
'dur. Bunu ardından
bağlantıda kullanarak yerel RPC URL'sini belirtiriz.
const connection = new Connection("http://127.0.0.1:8899", "confirmed");
Alternatif olarak, testnet veya devnet kullanmak isterseniz,
@solana/web3.js
'den clusterApiUrl
'yi içe aktarabilir ve bağlantıya bu şekilde
geçebilirsiniz:
const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
Devnet kullanmayı seçerseniz ve SOL ile airdrop sırasında sorun yaşarsanız,
initializeKeypair
fonksiyonuna keypairPath
parametresini ekleyebilirsiniz.
Bunu terminalde solana config get
komutunu çalıştırarak alabilir ve ardından
faucet.solana.com adresine gidip
adresinize bir SOL airdrop gerçekleştirebilirsiniz. Adresinizi terminalde
solana address
komutunu çalıştırarak alabilirsiniz.
3. Transfer ücreti ile bir mint oluşturun
Yeni bir dosyada createMintWithTransferFee
fonksiyonunu oluşturalım src/create-mint.ts
.
`transfer fee` uzantısıyla bir mint oluşturmak için üç talimata ihtiyacımız var: `SystemProgram.createAccount`, `createInitializeTransferFeeConfigInstruction` ve `createInitializeMintInstruction`.
Ayrıca yeni createMintWithTransferFee
fonksiyonumuzun aşağıdaki argümanlara sahip olmasını istiyoruz:
connection
: Bağlantı nesnesipayer
: İşlem için ödeyicimintKeypair
: Yeni mint için anahtar çiftidecimals
: Mint ondalık sayısıfeeBasisPoints
: Transfer ücreti için ücret baz puanlarımaxFee
: Transfer ücreti için maksimum ücret puanları
import {
sendAndConfirmTransaction,
Connection,
Keypair,
SystemProgram,
Transaction,
TransactionSignature,
} from "@solana/web3.js";
import {
ExtensionType,
createInitializeMintInstruction,
getMintLen,
TOKEN_2022_PROGRAM_ID,
createInitializeTransferFeeConfigInstruction,
} from "@solana/spl-token";
export async function createMintWithTransferFee(
connection: Connection,
payer: Keypair,
mintKeypair: Keypair,
decimals: number,
feeBasisPoints: number,
maxFee: bigint,
): Promise<TransactionSignature> {
const extensions = [ExtensionType.TransferFeeConfig];
const mintLength = getMintLen(extensions);
const mintLamports =
await connection.getMinimumBalanceForRentExemption(mintLength);
console.log("Transfer ücreti talimatıyla bir işlem oluşturuluyor...");
const mintTransaction = new Transaction().add(
SystemProgram.createAccount({
fromPubkey: payer.publicKey,
newAccountPubkey: mintKeypair.publicKey,
space: mintLength,
lamports: mintLamports,
programId: TOKEN_2022_PROGRAM_ID,
}),
createInitializeTransferFeeConfigInstruction(
mintKeypair.publicKey,
payer.publicKey,
payer.publicKey,
feeBasisPoints,
maxFee,
TOKEN_2022_PROGRAM_ID,
),
createInitializeMintInstruction(
mintKeypair.publicKey,
decimals,
payer.publicKey,
null,
TOKEN_2022_PROGRAM_ID,
),
);
console.log("İşlem gönderiliyor...");
const signature = await sendAndConfirmTransaction(
connection,
mintTransaction,
[payer, mintKeypair],
{ commitment: "finalized" },
);
console.log("İşlem gönderildi");
return signature;
}
Şimdi yeni fonksiyonumuzu src/index.ts
dosyasında içe aktaralım ve çağıralım. Dokuz ondalık basamağa, 1000 ücret baz puanına (yüzde 10) ve 5000 maksimum ücrete sahip bir mint oluşturacağız.
// TRANSFER ÜCRETİ İLE MINT OLUŞTUR
const decimals = 9;
const feeBasisPoints = 1000;
const maxFee = BigInt(5000);
await createMintWithTransferFee(
connection,
payer,
mintKeypair,
decimals,
feeBasisPoints,
maxFee,
);
Bu anahtarı başarılı bir şekilde çalıştığını kontrol etmek için kodu çalıştırın.
esrun src/index.ts
4. Bir ücret kasası hesabı oluşturun
Herhangi bir token transfer etmeden ve transfer ücretlerini biriktirmeden önce, tüm transfer ücretlerinin nihai alıcısı olacak bir "ücret kasası" oluşturun.
Basit olması açısından, ücret kasasını ödeyicimizin ilişkili token hesabı (ATA) olarak yapalım.
// ÜCRET KASASI HESABI OLUŞTUR
console.log("\nÜcret kasası hesabı oluşturuluyor...");
const feeVaultAccount = await createAssociatedTokenAccount(
connection,
payer,
mintKeypair.publicKey,
payer.publicKey,
{ commitment: "finalized" },
TOKEN_2022_PROGRAM_ID,
);
const initialBalance = (
await connection.getTokenAccountBalance(feeVaultAccount, "finalized")
).value.amount;
console.log("Mevcut ücret kasası bakiyesi: " + initialBalance + "\n\n");
Scripti yine çalıştırın, bakiyemizin sıfır olması gerektiğini kontrol edin.
esrun src/index.ts
5. İki token hesabı oluşturun ve birine mint edin
Şimdi source
ve destination
adını verdiğimiz iki test token hesabı oluşturalım. Ardından source
hesabına bazı tokenlar mint edelim.
Bunu createAccount
ve mintTo
fonksiyonlarını çağırarak yapabiliriz. Tam 10 token mint edeceğiz.
// TEST HESAPLARI OLUŞTURUN VE TOKENS MINT EDİN
console.log("Kaynak hesabı oluşturuluyor...");
const sourceKeypair = Keypair.generate();
const sourceAccount = await createAccount(
connection,
payer,
mint,
sourceKeypair.publicKey,
undefined,
{ commitment: "finalized" },
TOKEN_2022_PROGRAM_ID,
);
console.log("Hedef hesabı oluşturuluyor...");
const destinationKeypair = Keypair.generate();
const destinationAccount = await createAccount(
connection,
payer,
mint,
destinationKeypair.publicKey,
undefined,
{ commitment: "finalized" },
TOKEN_2022_PROGRAM_ID,
);
console.log("Kaynağa 10 token mint ediliyor...\n\n");
const amountToMint = 10 * 10 ** decimals;
await mintTo(
connection,
payer,
mint,
sourceAccount,
payer,
amountToMint,
[payer],
{ commitment: "finalized" },
TOKEN_2022_PROGRAM_ID,
);
İsterseniz kodu çalıştırarak her şeyin düzgün çalıştığını kontrol edin:
esrun src/index.ts
6. Bir token transferi yapın
Şimdi sourceAccount
hesabımızdan destinationAccount
hesabımıza 1 token transfer edelim ve neler olacağını görelim.
`transfer fee` uzantısı etkinleştirilmiş bir token transferi yapmak için `transferCheckedWithFee` çağrısını yapmamız gerekiyor. Bu, ne kadar göndereceğimize karar vermemizi ve ilişkili doğru ücreti hesaplamamızı gerektiriyor.
Bunu yapmak için, biraz matematik yapabiliriz:
Öncelikle, bir tam token göndermek aslında 1 * (10 ^ decimals)
token göndermek demektir.
Son olarak, ücretin maksimum ücretten fazla olup olmadığını kontrol etmemiz gerekiyor; eğer fazlaysa, transferCheckedWithFee
'yi maksimum ücreti belirterek çağırmalıyız.
const transferAmount = BigInt(1 * 10 ** decimals);
const basisPointFee =
(transferAmount * BigInt(feeBasisPoints)) / BigInt(10_000);
const fee = basisPointFee > maxFee ? maxFee : basisPointFee;
Tüm bu bilgilerle, bir saniye düşünün; bu işlem için nihai bakiyelerin ve alıkonulan miktarların ne olabileceğini düşünün.
Şimdi, bir token transfer edelim ve sonuçlanan bakiyeleri yazdıralım:
// TOKEN TRANSFER ET
console.log("Ücretli işlemle transfer yapılıyor...");
const transferAmount = BigInt(1 * 10 ** decimals);
const fee = (transferAmount * BigInt(feeBasisPoints)) / BigInt(10_000);
const transferSignature = await transferCheckedWithFee(
connection,
payer,
sourceAccount,
mint,
destinationAccount,
sourceKeypair.publicKey,
transferAmount,
decimals,
fee,
[sourceKeypair],
{ commitment: "finalized" },
TOKEN_2022_PROGRAM_ID,
);
const sourceAccountAfterTransfer = await getAccount(
connection,
sourceAccount,
undefined,
TOKEN_2022_PROGRAM_ID,
);
const destinationAccountAfterTransfer = await getAccount(
connection,
destinationAccount,
undefined,
TOKEN_2022_PROGRAM_ID,
);
const withheldAmountAfterTransfer = getTransferFeeAmount(
destinationAccountAfterTransfer,
);
console.log(`Kaynak Token Bakiyesi: ${sourceAccountAfterTransfer.amount}`);
console.log(
`Hedef Token Bakiyesi: ${destinationAccountAfterTransfer.amount}`,
);
console.log(
`Alıkonan Transfer Ücretleri: ${withheldAmountAfterTransfer?.withheldAmount}\n`,
);
Scripti çalıştırabilirsiniz:
esrun src/index.ts
Aşağıdaki sonuçları almanız gerekiyor:
Ücretli işlemle transfer yapılıyor...
Kaynak Token Bakiyesi: 9000000000
Hedef Token Bakiyesi: 999995000
Alıkonan Transfer Ücretleri: 5000
Küçük bir inceleme:
Ücret baz puanlarımız 1000, yani transfer edilen miktarın yüzde 10'u ücret olarak kullanılmalıdır. Bu durumda, 1,000,000,000'un yüzde 10'u 100,000,000 olup, bu 5000 maksimum ücretimizden çok daha büyük. Bu nedenle, 5000 tutarının tutulduğunu görüyoruz. Ayrıca, alıcının transfer ücretini "ödedigini" de unutmayın.
7. Ücretleri geri çekme
Alıcının hesabından ücret kasasına iki şekilde ücret toplayabiliriz.
İlk şekilde, alıcının hesabından doğrudan alıkonulan ücretleri `withdrawWithheldTokensFromAccounts` kullanarak ücret kasası hesabına çekiyoruz.
Şimdi, doğrudan hedef hesap üzerinden ücretleri geri çekelim ve sonuçlanan bakiyeleri kontrol edelim:
// DOĞRUDAN ÇEKİM
await withdrawWithheldTokensFromAccounts(
connection,
payer,
mint,
feeVaultAccount,
payer.publicKey,
[],
[destinationAccount],
undefined,
TOKEN_2022_PROGRAM_ID,
);
const withheldAccountAfterWithdraw = await getAccount(
connection,
destinationAccount,
undefined,
TOKEN_2022_PROGRAM_ID,
);
const withheldAmountAfterWithdraw = getTransferFeeAmount(
withheldAccountAfterWithdraw,
);
const feeVaultAfterWithdraw = await getAccount(
connection,
feeVaultAccount,
undefined,
TOKEN_2022_PROGRAM_ID,
);
console.log(
`Geri çekim sonrası alıkonan miktar: ${withheldAmountAfterWithdraw?.withheldAmount}`,
);
console.log(
`Geri çekim sonrası ücret kasası bakiyesi: ${feeVaultAfterWithdraw.amount}\n`,
);
Scripti çalıştırabilirsiniz:
esrun src/index.ts
Aşağıdaki sonuçları almanız gerekiyor:
Geri çekim sonrası alıkonan miktar: 0
Geri çekim sonrası ücret kasası bakiyesi: 5000
`withdrawWithheldTokensFromAccounts` fonksiyonu, alıkonulan tüm ücretleri tüm token hesaplarından toplamak için de kullanılabilir; ilk önce hepsini getirmeniz gerekir.
7.2 Hasat etme ve ardından geri çekme
Şimdi, alıkonulan ücretleri geri almak için "hasat etme" seçeneğine bakalım.
Bunu yapmak için, daha fazla ücret biriktirmek amacıyla daha fazla token transfer etmemiz gerekiyor. Bu sefer, transferChecked
fonksiyonunu kullanarak bir kısayol alacağız.
// TOKEN TRANSFER ET PT2
console.log("Ücretli işlemle transfer yapılıyor pt2...");
const secondTransferAmount = BigInt(1 * 10 ** decimals);
const secondTransferSignature = await transferChecked(
connection,
payer,
sourceAccount,
mint,
destinationAccount,
sourceKeypair,
secondTransferAmount,
decimals,
[],
undefined,
TOKEN_2022_PROGRAM_ID,
);
const sourceAccountAfterSecondTransfer = await getAccount(
connection,
sourceAccount,
undefined,
TOKEN_2022_PROGRAM_ID,
);
const destinationAccountAfterSecondTransfer = await getAccount(
connection,
destinationAccount,
undefined,
TOKEN_2022_PROGRAM_ID,
);
const withheldAmountAfterSecondTransfer = getTransferFeeAmount(
destinationAccountAfterSecondTransfer,
);
console.log(`Kaynak Token Bakiyesi: ${sourceAccountAfterSecondTransfer.amount}`);
console.log(
`Hedef Token Bakiyesi: ${destinationAccountAfterSecondTransfer.amount}`,
);
console.log(
`Alıkonan Transfer Ücretleri: ${withheldAmountAfterSecondTransfer?.withheldAmount}\n`,
);
Şimdi, tokenları mint hesabına geri hasat edelim. Bunu harvestWithheldTokensToMint
fonksiyonu ile yapacağız.
// HASAT EDİLEN TOKENSİ MİNTE YENİDEN İADESİ
await harvestWithheldTokensToMint(
connection,
payer,
mint,
[destinationAccount],
undefined,
TOKEN_2022_PROGRAM_ID,
);
Son olarak, bu ücretleri mintten withdrawWithheldTokensFromMint
fonksiyonu kullanarak geri çekelim.
// HASAT EDİLEN TOKENSİ GERİ ÇEK
await withdrawWithheldTokensFromMint(
connection,
payer,
mint,
feeVaultAccount,
payer,
[],
undefined,
TOKEN_2022_PROGRAM_ID,
);
Şimdi, bunu çalıştırın.
esrun src/index.ts
Her adımda bakiyeleri göreceksiniz.
Hepsi bu kadar! Başarıyla transfer ücreti ile bir mint oluşturmuş olduk. Eğer herhangi bir noktada takılırsanız, çalışır kodu solution
dalında bu depo'dan bulabilirsiniz.
Mücadele
Transfer ücreti etkinleştirilmiş bir mint oluşturun ve farklı ondalık basamaklar, ücret transfer puanları ve maksimum ücretlerle bazı tokenlar transfer edin.